
#import "XmlRpcDecoder.h"

//
// xmlrpc decoder
//
@interface XmlRpcDecoderImp : NSObject

//
// element type
//
enum
{
	//
	// invalid
	//
	XmlRpcElementTypeInvalid,
	
	//
	// array
	//
	XmlRpcElementTypeArray,
	
	//
	// dictionary
	//
	XmlRpcElementTypeDictionary,
	
	//
	// member
	//
	XmlRpcElementTypeMember,
	
	//
	// name
	//
	XmlRpcElementTypeName,
	
	//
	// int
	//
	XmlRpcElementTypeInteger,
	
	//
	// double
	//
	XmlRpcElementTypeDouble,
	
	//
	// boolean
	//
	XmlRpcElementTypeBoolean,
	
	//
	// string
	//
	XmlRpcElementTypeString,
	
	//
	// date
	//
	XmlRpcElementTypeDate,
	
	//
	// data
	//
	XmlRpcElementTypeData,
};

//
// result
//
@property(strong, nonatomic, readwrite) id									result;

//
// key
//
@property(strong, nonatomic, readwrite) NSString*							key;

//
// handler
//
@property(assign, nonatomic, readwrite) xmlSAXHandlerPtr					saxHandler;

//
// children
//
@property(strong, nonatomic, readwrite) NSMutableArray*						parserList;

//
// parent
//
@property(assign, nonatomic, readwrite) XmlRpcDecoderImp*					parent;

//
// type
//
@property(assign, nonatomic, readwrite) NSInteger							elementType;

//
// fault
//
@property(assign, nonatomic, readwrite) BOOL								fault;

@end

@implementation XmlRpcDecoderImp

//
// result
//
@synthesize result															= _result;

//
// key
//
@synthesize key																= _key;

//
// handler
//
@synthesize saxHandler														= _saxHandler;

//
// children
//
@synthesize parserList														= _parserList;

//
// parent
//
@synthesize parent															= _parent;

//
// element type
//
@synthesize elementType														= _elementType;

//
// fault
//
@synthesize fault															= _fault;

//
// init
//
- (id)initWithParent:(XmlRpcDecoderImp*)parent saxHandler:(xmlSAXHandlerPtr)saxHandler
{
	//
	// call super
	//
	self																	= [super init];
	if(!self)
		return nil;
	
	self.parent																= parent;
	self.saxHandler															= saxHandler;
	self.fault																= NO;
	
	return self;
}

//
// dealloc
//
- (void)dealloc
{
	self.result																= nil;
	self.key																= nil;
	self.parserList															= nil;
	
	[super dealloc];
}

//
// parse date
//
- (NSDate*)parseDate:(NSString*)dateString withFormat:(NSString*)dateFormat
{
	NSDateFormatter* formatter												= [[NSDateFormatter alloc] init];
	[formatter setDateFormat:dateFormat];
	NSDate* retValue														= [formatter dateFromString:dateString];
	[formatter release];
	return retValue;
}

//
// start element
//
- (void)startElement:(xmlChar const*)name attributes:(xmlChar const**)attributes
{
	if(!xmlStrcmp(name, (xmlChar const*)"fault"))
		self.fault															= YES;
	
	BOOL isValue															= !xmlStrcmp(name, (xmlChar const*)"value");
	BOOL isMember															= !xmlStrcmp(name, (xmlChar const*)"member");
	BOOL isName																= !xmlStrcmp(name, (xmlChar const*)"name");
	if(isValue || isMember || isName)
	{
		XmlRpcDecoderImp* decoder											= [[[XmlRpcDecoderImp alloc] initWithParent:self saxHandler:self.saxHandler] autorelease];
		if(isMember)
			decoder.elementType												= XmlRpcElementTypeMember;
		else if(isName)
			decoder.elementType												= XmlRpcElementTypeName;
		
		if(!self.parserList)
			self.parserList													= [NSMutableArray arrayWithCapacity:1];
		
		[self.parserList addObject:decoder];
		self.saxHandler->_private											= decoder;
		return;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"array"))
	{
		self.result															= [NSMutableArray array];
		self.elementType													= XmlRpcElementTypeArray;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"struct"))
	{
		self.result															= [NSMutableDictionary dictionary];
		self.elementType													= XmlRpcElementTypeDictionary;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"int") || !xmlStrcmp(name, (xmlChar const*)"i4") || !xmlStrcmp(name, (xmlChar const*)"i8"))
	{
		self.elementType													= XmlRpcElementTypeInteger;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"double"))
	{
		self.elementType													= XmlRpcElementTypeDouble;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"boolean"))
	{
		self.elementType													= XmlRpcElementTypeBoolean;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"string"))
	{
		self.elementType													= XmlRpcElementTypeString;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"dateTime.iso8601"))
	{
		self.elementType													= XmlRpcElementTypeDate;
	}
	else if(!xmlStrcmp(name, (xmlChar const*)"base64"))
	{
		self.elementType													= XmlRpcElementTypeData;
	}
}

//
// end element
//
- (void)endElement:(xmlChar const*)name
{
	BOOL isValue															= !xmlStrcmp(name, (xmlChar const*)"value");
	BOOL isMember															= !xmlStrcmp(name, (xmlChar const*)"member");
	BOOL isName																= !xmlStrcmp(name, (xmlChar const*)"name");
	if(isValue || isMember || isName)
	{
		if(self.elementType != XmlRpcElementTypeArray && self.elementType != XmlRpcElementTypeDictionary && self.elementType != XmlRpcElementTypeMember)
			self.result														= [self.result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
		
		switch(self.elementType)
		{
		case XmlRpcElementTypeInteger:
			self.result														= [NSNumber numberWithLongLong:[self.result longLongValue]];
			break;
			
		case XmlRpcElementTypeDouble:
			self.result														= [NSNumber numberWithDouble:[self.result doubleValue]];
			break;
			
		case XmlRpcElementTypeBoolean:
			self.result														= [NSNumber numberWithBool:[self.result isEqualToString:@"1"]];
			break;
			
		case XmlRpcElementTypeDate:
			{
				id temp														= [self parseDate:self.result withFormat:@"yyyyMMdd'T'HH:mm:ss"];
				if(temp)
					self.result												= temp;
				else
					self.result												= [self parseDate:self.result withFormat:@"yyyy'-'MM'-'dd'T'HH:mm:ss"];
			}
			break;
			
		case XmlRpcElementTypeData:
			self.result														= [self.result decodeBase64];
			break;
			
		default:
		case XmlRpcElementTypeString:
			if(!self.result)
				self.result													= @"";
				
		case XmlRpcElementTypeName:
			break;
		}
	
		if(self.parent && self.result)
		{
			switch(self.parent.elementType)
			{
			case XmlRpcElementTypeArray:
				[self.parent.result addObject:self.result];
				break;
				
			case XmlRpcElementTypeDictionary:
				[self.parent.result setObject:self.result forKey:self.key];
				break;
				
			case XmlRpcElementTypeMember:
				if(self.elementType == XmlRpcElementTypeName)
					self.parent.key											= self.result;
				else
					self.parent.result										= self.result;
			}
		}

		self.saxHandler->_private											= self.parent;
	}
}

//
// characters
//
- (void)characters:(xmlChar const*)buffer length:(int)length
{
	if(self.elementType == XmlRpcElementTypeArray || self.elementType == XmlRpcElementTypeDictionary || self.elementType == XmlRpcElementTypeMember)
		return;
	
	if(!self.result)
		self.result															= [NSMutableString string];
	
	NSString* newString														= [[NSString alloc] initWithBytes:buffer length:length encoding:NSUTF8StringEncoding];
	[self.result appendString:newString];
	[newString release];
}

//
// get entity helper
//
static xmlEntityPtr getEntityHelper(void* userData, xmlChar const* name)
{
	return xmlGetPredefinedEntity(name);
}

//
// start element helper
//
static void startElementHelper(void* userData, xmlChar const* name, xmlChar const** attributes)
{
	[(XmlRpcDecoderImp*)((xmlSAXHandlerPtr)userData)->_private startElement:name attributes:attributes];
}

//
// end element helper
//
static void endElementHelper(void* userData, xmlChar const* name)
{
	[(XmlRpcDecoderImp*)((xmlSAXHandlerPtr)userData)->_private endElement:name];
}

//
// characters helper
//
static void charactersHelper(void* userData, xmlChar const* buffer, int length)
{
	[(XmlRpcDecoderImp*)((xmlSAXHandlerPtr)userData)->_private characters:buffer length:length];
}

//
// decode data
//
- (id)decodeData:(NSData*)xmlData
{
	xmlSAXHandler handler													= {0};
	handler.getEntity														= &getEntityHelper;
	handler.startElement													= &startElementHelper;
	handler.endElement														= &endElementHelper;
	handler.characters														= &charactersHelper;
	handler._private														= self;
	self.saxHandler															= &handler;
	xmlSAXUserParseMemory(self.saxHandler, self.saxHandler, (char const*)xmlData.bytes, xmlData.length);
	return self.parserList.count ? [[[[self.parserList objectAtIndex:0] result] copy] autorelease]: nil;
}
@end

@implementation XmlRpcDecoder

//
// decode data
//
+ (id)decodeData:(NSData *)xmlData isFault:(BOOL*)isFault
{
	XmlRpcDecoderImp* decoder												= [[XmlRpcDecoderImp alloc] init];
	id retValue																= [decoder decodeData:xmlData];
	if(isFault)
		*isFault															= decoder.fault;
	[decoder release];
	return retValue;
}

@end
